package connection_modul;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Vector;

/**
 * Klasa reprezentuje konekciju ka bazi podataka. Prava, fizicka, konekcija
 * ka bazi podataka je kreirana prilikom inicijalizacije objekta
 * DatabaseModul klase. Ova klasa koristi postojecu konekciju za kreiranje
 * odgovarajucih upita i uopste za manipulaciju bazom.
 * Pozeljno je objekat ove klase vezati za objekat koji reprezentuje 
 * korisnika i koji je nezavisna nit, kako bi pooling funkcionisao
 * na predvidjeni nacin.
 * 
 * @authors Uros Krkic & Martin Jondzic
 */
public class DatabaseConnection {
	
	private Statement statement = null;
	
	private ResultSet resultSet = null;
	
	private ResultSetMetaData metaData = null;
	
	private Connection dbConnection = null;
	
	private DatabaseModul dbModul = null;
	
	public ArrayList<Vector> data = new ArrayList<Vector>();
	
	public Vector<String> columnNames = new Vector<String>();
	
	
	
	
	
	/**
	 * Konstruktor klase kome se prosledjuje objekat DatabaseModul.
	 * @param databaseModul
	 */
	public DatabaseConnection(DatabaseModul dbModul) {
		this.dbModul = dbModul;
		initializeParameters();
	}
	
	
	
	private void initializeParameters() {

		if(this.dbModul == null)
			return;
		// uzima se konekcija iz pool-a
		this.dbConnection = dbModul.retrieveConnection();
		try {
			
			this.statement = dbConnection.createStatement();
			
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	

	/**
	 * Test metoda za sql upite.
	 * @param SQLQuery
	 */
	public void doStatement(String SQLQuery) throws SQLException {
//		System.out.println(statement);
		this.resultSet = statement.executeQuery(SQLQuery);
		retrieveDataFromResultSet(resultSet);
//		this.metaData = resultSet.getMetaData();
	}

	
	
	/**
	 * Metoda izvrsava UPDATE statement-e.
	 * 
	 * @throws SQLException
	 * @param SQLUpdate - UPDATE statement
	 * @author Uros Krkic
	 */
	public void doUpdateStatement(String SQLUpdate) throws SQLException {
		
		statement.executeUpdate(SQLUpdate);
	}
	

	
	/**
	 * Stampanje sadrzaja baze.
	 * 
	 * @param 
	 */
	public int printResultSet() {
		try {
			int readRows = 0;

			while(resultSet.next()) {
				readRows++;
				
				System.out.println();
				String user = resultSet.getString("username");
				String pass = resultSet.getString("password");
				String nick = resultSet.getString("nickname");
				String email = resultSet.getString("email");
				int port = resultSet.getInt("port");
				String ipAddress = resultSet.getString("ip_address");
				String status = resultSet.getString("reg_date");
				String offMessages = resultSet.getString("off_message");
				
				System.out.println("podaci" +user+"\t"+pass+"\t"+nick+"\t"+email+"\t"+port
						+"\t"+ipAddress+"\t"+status+"\t"+offMessages);
			}
			
		return readRows;	
		} catch (SQLException e) {
			e.printStackTrace();
			return -1;
		}
	}
	
	

	
	/**
	 * Metod uzima podatke iz ResultSet-a i koristi metapodatke iz DBMS-a.
	 * !!! Bez counter-a redova.
	 * @param resultSet - ResultSet iz SQL upita
	 * @author Uros Krkic
	 */
	@SuppressWarnings("unchecked")
	private void retrieveDataFromResultSet(ResultSet resultSet) {
		Vector row;
		
		ResultSetMetaData metaData;
		
		try {
			metaData = resultSet.getMetaData();
			
			for (int i = 1; i <= metaData.getColumnCount(); i++) {
				
				this.columnNames.add(i-1, metaData.getColumnName(i));
			}
					
			while ( resultSet.next() ) {
				
				row = new Vector();

				for (int i = 1; i <= metaData.getColumnCount(); i++) {
					
					switch ( metaData.getColumnType(i) ) {
						case Types.INTEGER: {
							row.add(i-1, resultSet.getInt(i) );
							break;
						}
						case Types.BIGINT: {
							row.add(i-1, resultSet.getLong(i));
							break;
						}
						case Types.CHAR:
						case Types.VARCHAR: {
							String str = resultSet.getString(i);
							row.add(i-1, str == null ? null : str.trim());
							break;
						}
						case Types.BIT:
						case Types.BOOLEAN: {
							row.add(i-1, resultSet.getBoolean(i) );
							break;
						}
						case Types.FLOAT: {
							row.add(i-1, resultSet.getFloat(i) );
							break;
						}
						case Types.DOUBLE: {
							row.add(i-1, resultSet.getDouble(i) );
							break;
						}
						case Types.DATE: {
							row.add(i-1, resultSet.getDate(i) );
							break;
						}
						case Types.TIME: {
							row.add(i-1, resultSet.getTime(i) );
							break;
						}
						case Types.TIMESTAMP: {
							row.add(i-1, resultSet.getTimestamp(i) );
							break;
						}
					}
				}
				
				data.add(row);
			}

		}
		catch (SQLException sqle) {
			// obraditi gresku
			sqle.printStackTrace();
		}
	}
	

	
	
	/**
	 * Stampanje meta podataka dobijenih sql upitom.
	 */
	public void printMetaData() {
		int count = 0;
		try {
			count = metaData.getColumnCount();
			
		} catch (SQLException e) {
			e.printStackTrace();
			return;
		}
		
		for(int i = 0; i < count; i++) {
			try {
				System.out.print(metaData.getColumnName(i+1)+"\t");
				
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Vraca konekciju u pool.
	 */
	public void closeConnection() {
		try {
			resultSet.close();
			statement.close();
			
		} catch (SQLException e) {
//			e.printStackTrace();
		}
		catch (NullPointerException npu) {
//			npu.printStackTrace();
		}
		
		dbModul.backToPool(dbConnection);
	}
	
	
	
	public ResultSet getResultSet() {
		return resultSet;
	}	
	
	public void setResultSet(ResultSet resultSet) {
		this.resultSet = resultSet;
	}	
	
	
	/**
	 * Vrsi vracanje transakcije do, prethodno postavljenog, set pointa.
	 * Pozeljno je da se poziva nakon neuspelog izvrsenja transakcije, kako bi sve prethodne
	 * promene u bazi podataka, do postavljenog save pointa,  bile eliminisane.
	 * 
	 * @throws SQLException 
	 */
	public void doRollBack() {
		try {
			dbConnection.rollback();
			
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Metoda vrsi commit transakcije. 
	 *
	 */
	public void doCommit() {
		try {
			dbConnection.commit();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Metod prazni skladiste podataka za SQL upit.
	 * @author Uros Krkic
	 */
	public void clearDataStore() {
		this.data.clear();
		this.columnNames.clear();
	}
	
	
	/**
	 * Oznavaca da li je stvarna (fizicka) konekcija ka bazi zatvorena.
	 * @return true, ako jeste, inace false
	 */
	public boolean isClosed() {
		boolean flag = true;
		try {
			flag = dbConnection.isClosed();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return flag;
	}
	
}
